package COMM232;

import java.io.BufferedInputStream; //Fluxo de entrada bufferizado.
import java.io.BufferedOutputStream; //Fluxo de sa�da bufferizado.
import java.io.IOException; //Exce��o de IO.
import gnu.io.SerialPortEventListener; //Interface que escuta eventos da porta serial.
import gnu.io.SerialPortEvent; //Evento de porta serial.
import gnu.io.SerialPort; //Abstra��o de porta serial.
import gnu.io.CommPortIdentifier; //Indentifica uma porta serial.
import gnu.io.PortInUseException; //Exce��o de porta em uso.
import gnu.io.UnsupportedCommOperationException; //Exce��o de opera��o n�o suportada pela porta serial.
import java.util.TooManyListenersException; //Exce��o de muitos ouvintes.
import java.util.Enumeration; //Enumerador.

/**
 * Classe responsavel pelo tratamento de baixo nivel da comunicacao serial.
 * 
 * @author Emerson Beserra
 * @since 15:33 05/03/2009
 */
public class DriverSerial implements SerialPortEventListener 
{
	
//Atributos e constantes do driver serial.--------------------------------------
	
	/**
	 * Representa o m�ximo de bytes lidos da porta serial, o n�mero 200, expressa apenas um 
	 * hipot�tico n�mero m�ximo de bytes.
	 */
	private final int MAXIMO_BYTES_LIDOS = 200;
	/**
	* Pacote de bytes recebido pela porta serial.
	*/
	private char[] pacote;
	/**
	 * � o tempo que o computador deve esperar para que todos os bytes estejam no buffer de entrada
	 * da porta serial.
	 */
	private int delayLeitura;
	/**
	 * Fluxo de entrada dos dados a partir da porta serial.
	 */
	private BufferedInputStream entrada;
	/**
	 * Fluxo de saida dos dados para a porta serial.
	 */
	private BufferedOutputStream saida;
	/**
	 * Lista de identificadores de portas (seriais ou paralelas).
	 */
	private CommPortIdentifier[] identificadoresPortas;
	/**
	 * Porta serial pela qual est� ocorrendo a comunica��o entre microcomputador e UPS.
	 */
	private SerialPort portaSerial;
	/**
	 * Diz quantas portas seriais existem no sistema.
	 */
	private int quantidadePortas;
	/**
	 * �ndice que indica a porta serial atualmente utilizada.
	 */
	private int indice = -1;
	/**
	 * Objeto que ir� escutar eventos de notifica��o do driver.
	 */
	private DriverListener ouvinteDriver;
	
	private int idx;
	
	private Utils utils;
	
	private boolean txRdy;
	            
//------------------------------------------------------------------------------    
    
//Construtores.-----------------------------------------------------------------    
    
	/**
     * Construtor padr�o do driver.
     */
    public DriverSerial(Utils utils) 
    { 
    	txRdy = false;
    	this.utils = utils;
    	pacote = new char[MAXIMO_BYTES_LIDOS];
    	
    	Enumeration enumeraPortas = CommPortIdentifier.getPortIdentifiers(); //Retorna um objeto Enumeration que contem um 
                                                        	                 //objeto CommPortIndentifier para cada porta no 
                                                                             //sistema.
    	identificadoresPortas = new CommPortIdentifier[10]; //Numero arbitrario de portas que possam
    	                                                    //existir no sistema.
    	
       	quantidadePortas = 0;
       	CommPortIdentifier portaId;
       	
		while (enumeraPortas.hasMoreElements()) 
		{
			portaId = (CommPortIdentifier) enumeraPortas.nextElement(); //Percorre as portas no objeto Enumeration enumerador.
			if (portaId.getPortType() == CommPortIdentifier.PORT_SERIAL) //Verifica se eh uma porta serial.
		        identificadoresPortas[quantidadePortas++] = portaId; //Insere a porta serial no array identificador de portas.
		}//end while		
		
    }//end DriverSerial()-------------------------------------------------------
    
//------------------------------------------------------------------------------
 
    public boolean open(SerialParameters param)
    {
    	CommPortIdentifier portaSerialEscolhida = param.getCom();
    	int tempoEspera = 200;
    	int baudRate = param.getBaudRate();
    	int dataBits = param.getDataBits();
    	int stopBit = param.getStopBits();
    	int paridade = param.getParity();
    	delayLeitura = 200;
    	
    	try 
    	{
			portaSerial = (SerialPort) portaSerialEscolhida.open("Serial Driver - JAVA", tempoEspera); //Abre a porta, primeiro argumento e o nome da aplicacao que abre a porta, o segundo e o tempo maximo de espera pela abertura da porta.
		} 
    	catch (PortInUseException piue) 
    	{ 
			//System.err.println(piue.getMessage());
			//piue.printStackTrace();
			return false;
		} 
		
   		try 
   		{
			entrada = new BufferedInputStream(portaSerial.getInputStream()); //Obtem o fluxo de entrada da porta serial.
			saida = new BufferedOutputStream(portaSerial.getOutputStream()); //Obtem o fluxo de sa�da da porta serial.
		}
   		catch (IOException ioe) 
		{
			//System.err.println(ioe.getMessage());
			//ioe.printStackTrace();
			return false;
		} 

		try 
		{
			portaSerial.addEventListener(this); //Registra o ouvinte dos eventos (esta classe) da porta serial.
		}
		catch (TooManyListenersException tmle) 
		{
			//.err.println(tmle.getMessage());
			tmle.printStackTrace();
			return false;
		} 

		try 
		{
			portaSerial.setSerialPortParams(baudRate, dataBits, stopBit, paridade); 
			portaSerial.setRTS(true); //REQUEST-TO-SEND is ON
		} 
		catch (UnsupportedCommOperationException ucoe) 
		{ 
			//System.err.println(ucoe.getMessage());
			ucoe.printStackTrace();
			return false;
		}
				
		portaSerial.notifyOnDataAvailable(true); //Dispara evento de serial quando houverem dados no buffer de entrada.
		portaSerial.notifyOnOutputEmpty(true);
		return true;
    }
    
//M�todos do driver serial.---------------------------------------------------
        
    /**
	 * O array de <code>String</code> passado como argumento contem as configura��es para abertura de
	 * uma porta serial. O primeiro elemento do array deve ser o nome da porta, o segundo elemento 
	 * deve ser o tempo m�ximo para esperar a conex�o em milisegundos, o terceiro elemento deve ser o
	 * baud rate, o quarto elemento deve ser a quantidade de bits de dados, o quinto elemento deve 
	 * ser a dura��o do stop bit, o sexto elemento a paridade, e o s�timo � o delay de leitura. 
	 * @param parametrosConfiguracao Par�metros de configura��o.
	 * @return Um <code>boolean</code> que indica se a conex�o foi estabelecida. 
	 */   
    public boolean open(String[] parametrosConfiguracao) 
    {
    	
    	String portaSerialEscolhida = parametrosConfiguracao[0];
    	int tempoEspera = Integer.parseInt(parametrosConfiguracao[1]);
    	int baudRate = Integer.parseInt(parametrosConfiguracao[2]);
    	int dataBits = Integer.parseInt(parametrosConfiguracao[3]);
    	int stopBit = Integer.parseInt(parametrosConfiguracao[4]);
    	int paridade = Integer.parseInt(parametrosConfiguracao[5]);
    	delayLeitura = Integer.parseInt(parametrosConfiguracao[6]);
    	
    	switch (dataBits) {
    		case SerialPort.DATABITS_5: break;
    		case SerialPort.DATABITS_6: break;
    		case SerialPort.DATABITS_7: break;
    		case SerialPort.DATABITS_8: break;
    		default: return false; //O par�metro dataBits est� errado.
    	}
    	
    	switch (stopBit) {
			case SerialPort.STOPBITS_1: break;
			case SerialPort.STOPBITS_1_5: break;
			case SerialPort.STOPBITS_2: break;
			default: return false; //O par�metro stopBit est� errado.
    	}
    	
    	switch (paridade) {
			case SerialPort.PARITY_EVEN: break;
			case SerialPort.PARITY_MARK: break;
			case SerialPort.PARITY_NONE: break;
			case SerialPort.PARITY_ODD: break;
			case SerialPort.PARITY_SPACE: break;
			default: return false; //O par�metro paridade est� errado.
    	}
    	
    	int i = 0;
    	boolean encontrado = false;
    	
    	while (i < quantidadePortas) {
    		
    		if (identificadoresPortas[i].getName().equals(portaSerialEscolhida)) {
    			encontrado = true;
    			indice = i;
    		}
    		
    		i++;    		
    	}
    	
    	if (!encontrado)
    		return false; //A porta com nome passado n�o foi encontrada no sistema.
    	
    	try {
			portaSerial = (SerialPort) identificadoresPortas[indice].open("DriverSerialUPS", tempoEspera); //Abre a porta, primeiro argumento e
                                                                                                     //o nome da aplicacao que abre a
                                                                                                     //a porta, o segundo e o tempo
                                                                                                     //maximo de espera pela abertura da
                                                                                                     //porta.
		} catch (PortInUseException piue) { //Se a porta estiver em uso...
			//System.err.println(piue.getMessage());
			//piue.printStackTrace();
			return false;
		} 
		
   		try {
			entrada = new BufferedInputStream(portaSerial.getInputStream()); //Obtem o fluxo de entrada da porta serial.
			saida = new BufferedOutputStream(portaSerial.getOutputStream()); //Obtem o fluxo de sa�da da porta serial.
		} catch (IOException ioe) { //Se ocorrer algum erro de IO.
			//System.err.println(ioe.getMessage());
			ioe.printStackTrace();
			return false;
		} 

		try {
			portaSerial.addEventListener(this); //Registra o ouvinte dos eventos (esta classe) da porta serial.
		} catch (TooManyListenersException tmle) { //Se houver mais ouvintes que o permitido... 
			//.err.println(tmle.getMessage());
			//tmle.printStackTrace();
			return false;
		} 

		try {
			portaSerial.setSerialPortParams(baudRate, //Configura baud rate da porta,
											dataBits, //n�mero de bits de dados,
											stopBit, //stop bit,
											paridade); //se haver� bit de paridade.
			
			portaSerial.setRTS(true); //habilita o pino de RTS para ter compatibilidade com as seriais isoladas
			
		} catch (UnsupportedCommOperationException ucoe) { //Se as opera��es n�o forem suportadas... 
			//System.err.println(ucoe.getMessage());
			//ucoe.printStackTrace();
			return false;
		} catch (Exception e) { //Exce��o que � levantada no Linux, mas ainda n�o foi identificado seu tipo.
			//System.err.println(e.getMessage());
			//e.printStackTrace();
			return false;
		}
		
		portaSerial.notifyOnDataAvailable(true); //Dispara evento de serial quando houverem dados no buffer de entrada.
		return true;
		
	} //Fim open()
    
    /**
     * M�todo que fecha a porta serial atualmente usada.
     * @return Um <code>boolean</code> indicando se a porta foi fechada com sucesso.
     */
    public boolean close() 
    {
    	try 
    	{
    		entrada.close(); //Libera o recurso para o sistema.
    		entrada = null;
    		saida.close(); //Libera o recurso para o sistema.
    		saida = null;
    		portaSerial.close(); //Libera o recurso para o sistema.
    		portaSerial = null;
    		indice = -1; 
    	}
    	catch (IOException ioe) 
    	{  
    		//System.err.println(ioe.getMessage());
    		ioe.printStackTrace(); 
    		return false;
    	} 
    	return true;
    } //Fim close()
    
    /**
     * Caso haja a necessidade da recepcao ser levada um nivel acima.
     * @return
     */
    public void disableSerialEvent()
    {
    	portaSerial.notifyOnDataAvailable(false);
    }
    
    public void enableSerialEvent()
    {
    	portaSerial.notifyOnDataAvailable(true);
    }
    
    /**
     * M�todo que trata eventos de porta serial.
     * @param spe Um <code>SerialPortEvent</code> (evento de porta serial).
     */  
    public void serialEvent(SerialPortEvent spe)
    { 
		switch( spe.getEventType() ) 
		{ 
         	case SerialPortEvent.BI: //Tipo de interrup��o da comunica��o.
		    case SerialPortEvent.OE: //Tipo de erro de escrita.
		    case SerialPortEvent.FE: //Erro de framing.
         	case SerialPortEvent.PE: //Erro na paridade.
         	case SerialPortEvent.CD: //Detectadar portadora.
         	case SerialPortEvent.CTS: //Porta indica que est� pronta para dados.
         	case SerialPortEvent.DSR: //Conjunto de dados pronto.
         	case SerialPortEvent.RI: //Indicador de chamada.
         		break;//DO NOTHING
         	case SerialPortEvent.OUTPUT_BUFFER_EMPTY: //Buffer de sa�da vazio.
         		txRdy = true;
            break; 
         	case SerialPortEvent.DATA_AVAILABLE: //Dados disponiveis na porta serial.
         		
         		try 
         		{
         			//Thread.sleep(delayLeitura); //O tempo para o Solis enviar o pacote todo.
         			//System.out.println("Delay:"+delayLeitura);		                     			
         			int bytes = entrada.available(); //Quantidade de bytes dispon�vel no buffer de entrada.
         			         			
         			for (int i = 0; i < bytes; i++) 
         			{
         				pacote[idx++] = (char) entrada.read();
         				//System.out.print( pacote[i] );
         				try
         				{
         					char c = pacote[idx - 1];
         					if( ( c == utils.getCh1() ) || ( c == utils.getCh2() ) || ( c == utils.getCh3() ) )
         					{
         						ouvinteDriver.isrSerial();
         					}//end if
         				}//end try
         				catch (Exception e) 
         				{
         					e.printStackTrace(); 
         				}
         			}//end for
         			         			
         			//System.out.println("\n-------------------------------LEU-------------------------------");
         			
//         			pacote[bytes] = (char) bytes; //Coloca a quantidade de bytes lidos como �ltimo 
//         								   //elemento do array.
//         				         				
//         			for (int i = bytes + 1; i < pacote.length; i++)
//         				pacote[i] = (char) -1; //Todos os elementos restantes s�o configurados como -1.
         				         				         			
         		} 
         		catch( IOException ioe ) 
         		{ 
         			ioe.printStackTrace();
         		} 
//         		catch( InterruptedException ie ) 
//         		{  
//         			ie.printStackTrace();
//         		} 
//         		catch (Exception e) 
//         		{
//					e.printStackTrace();
//				}
		}//end switch
	} //Fim serialEvent()
    
    /**
     * M�todo que escreve na porta serial.
     * @param pct Um <code>int[]</code> que representa o pacote a ser escrito.
     * @return Um <code>boolean</code> que indica se a escrita teve sucesso.
     */ 
    public boolean sendBytes(int[] pct) 
    {
		
    		try 
    		{
       			for (int i = 0; i < pct.length; i++)
       			{ 
    			   saida.write(pct[i]);
    			   //System.out.print(pct[i]);
    			}
    			saida.flush(); //Esvazia o buffer de sa�da para a porta serial.
    			//System.out.println("\n-------------------------------ESCREVEU-------------------------------");
    			return true;
    		} 
    		catch (IOException ioe) 
    		{ 
    			ioe.printStackTrace();
    			return false;
    		}
    } //Fim sendBytes()
    
    /**
     * M�todo que escreve na porta serial.
     * @param pct Um <code>int[]</code> que representa o pacote a ser escrito.
     * @return Um <code>boolean</code> que indica se a escrita teve sucesso.
     */ 
    public boolean sendBytes(char[] pct) 
    {
		//String dado;
    		try 
    		{
       			for (int i = 0; i < pct.length; i++)
       			{ 
    			   saida.write(pct[i]);
    			   //dado = Integer.toHexString( (int) pct[i]);
    				//System.out.print(dado + ", ");
    			   //System.out.print(pct[i]);
    			}
    			saida.flush(); //Esvazia o buffer de sa�da para a porta serial.
    			//System.out.println("\n-------------------------------ESCREVEU-------------------------------");
    			return true;
    		} 
    		catch (IOException ioe) 
    		{ 
    			ioe.printStackTrace();
    			return false;
    		}
    } //Fim sendBytes()
    
    
    /**
     * M�todo que escreve na porta serial.
     * @param pct Um <code>int[]</code> que representa o pacote a ser escrito.
     * @return Um <code>boolean</code> que indica se a escrita teve sucesso.
     */ 
    public boolean sendByte(int pct) 
    {
		
    		try 
    		{
    			saida.write(pct);
    			saida.flush(); //Esvazia o buffer de sa�da para a porta serial.
    			return true;
    		} 
    		catch (IOException ioe) 
    		{ 
    			ioe.printStackTrace();
    			return false;
    		}
    } //Fim sendBytes()
    
    
    /**
     * M�todo que escreve na porta serial.
     * @param pct Um <code>int[]</code> que representa o pacote a ser escrito.
     * @return Um <code>boolean</code> que indica se a escrita teve sucesso.
     */ 
    public boolean sendBytes(byte[] pct) 
    {
		
    		try 
    		{
       			for (int i = 0; i < pct.length; i++)
       			{ 
    			   saida.write(pct[i]);
    			   System.out.print(pct[i]);
    			}
    			saida.flush(); //Esvazia o buffer de sa�da para a porta serial.
    			//System.out.println("\n-------------------------------ESCREVEU-------------------------------");
    			return true;
    		} 
    		catch (IOException ioe) 
    		{ 
    			ioe.printStackTrace();
    			return false;
    		}
    } //Fim sendBytes()
    
    /**
     * 
     * @param pct
     * @return
     */
    public boolean sendBytes(String pct) 
    {
		
    		try 
    		{
       			for (int i = 0; i < pct.length(); i++)
       			{ 
    			   saida.write( pct.charAt(i) );
    			   //System.out.print( pct.charAt(i) );
    			}
       			//saida.write( 0x0D );
    			saida.flush(); //Esvazia o buffer de sa�da para a porta serial.
    			//System.out.println("\n-------------------------------ESCREVEU-------------------------------");
    			return true;
    		} 
    		catch (IOException ioe) 
    		{ 
    			ioe.printStackTrace();
    			return false;
    		}
    } //Fim sendBytes()
    
    /**
     * M�todo que escreve na porta serial.
     * @param pct Um <code>int[]</code> que representa o pacote a ser escrito.
     * @param delay Um <code>int</code> que representa o delay entre a escrita de cada byte. 
     * @return Um <code>boolean</code> que indica se a escrita teve sucesso.
     */ 
    public boolean sendBytes(int[] pct, int delay) 
    {
		
    		try {
       			for (int i = 0; i < pct.length; i++){ //Escreve o pacote todo byte por byte.
    			   saida.write(pct[i]);
    			   Thread.sleep(delay);
    			   System.out.print(pct[i] + " ");
    			}
    			saida.flush(); //Esvazia o buffer de sa�da para a porta serial.
    			System.out.println("\n-------------------------------ESCREVEU-------------------------------");
    			return true;
    		} 
    		catch (IOException ioe) 
    		{
    			//System.err.println(ioe.getMessage());
    			ioe.printStackTrace();
    			return false;
    		} 
    		catch (InterruptedException e) 
    		{
				e.printStackTrace();
				return false;
			}
    } //Fim sendBytes()
    
    /**
	 * Este m�todo retorna os bytes lidos a partir da interface de comunica��o. O �ltimo valor no
	 * array diferente de -1 � a quantidade de bytes lidos.
	 * @return pacote S�o os bytes retornados da interface.
	 */
    public char[] getBytes()
    {
    	return pacote; 
    }
    
//---------------------------------------------------------------------------------------------------------------------    
    
//Finalizador.---------------------------------------------------------------------------------------------------------    
    
    /**
     * M�todo finalizador para liberar os recursos utilizados.
     */
    protected void finalize() 
    {
    	close();
    } //Fim finalize()
    
//---------------------------------------------------------------------------------------------------------------------
    
//SETs-----------------------------------------------------------------------------------------------------------------
    
    public void flushBuffer()
    {
    	for(int i = 0; i < pacote.length; i++)
    		pacote[i] = 0x00;
    	idx = 0;
    }
    
//---------------------------------------------------------------------------------------------------------------------
    
//GETs-----------------------------------------------------------------------------------------------------------------
    
    /**
     * Retorna as portas seriais do sistema.
     * @return Um <code>String[]</code> que lista as portas seriais dispon�veis. 
     */
    public String[] getPortas() {
    	
    	String[] portas = new String[quantidadePortas];
    	
    	for (int i = 0; i < portas.length; i++)
    		portas[i] = identificadoresPortas[i].getName();
    		
    	return portas;
    	
    } //Fim getPortasSeriais()
    
    /**
     * Retorna o �ndice da porta serial atualmente utilizada.
     * @return Um <code>int</code> que indica a porta serial atual.
     */ 
    public int getIndicePortaAtual()
    {
    	return indice; 
    }
    
    /**
     * Retorna o nome da porta serial atualmente usada ou null se n�o estiver conectado � alguma porta.
     * @return Um <code>String</code>.
     */
    public String getNomePortaAtual() 
    { 
    	
    	if (portaSerial != null)
    		return portaSerial.getName(); 
    	else
    		return null;
    	
    } //Fim getNomePortaAtual()
    
    /**
     * M�todo que registra o ouvinte de eventos deste driver.
     * @param ouvinteDriver Um <code>DriverListener</code> que escuta o driver.
     */
    public void addDriverListener(DriverListener ouvinteDriver) 
    {
    	this.ouvinteDriver = ouvinteDriver;
    }

	/**
	 * @return the idx
	 */
	public int getIdx()
	{
		return idx;
	}

	/**
	 * @param idx the idx to set
	 */
	public void setIdx(int idx)
	{
		this.idx = idx;
	}

	/**
	 * @return the txRdy
	 */
	public boolean isTxRdy()
	{
		if(txRdy )
		{
			txRdy = false;
			return true;
		}
		return false;
	}
	
	public BufferedOutputStream getSerialDataOut()
	{
		return saida;
	}
	
	public BufferedInputStream getSerialDataIn()
	{
		return entrada;
	}
    	    
//---------------------------------------------------------------------------------------------------------------------    
 	
} //Fim Driver
